package org.zoomdev.zoom.protobuf.modules;

import io.protostuff.runtime.RuntimeSchema;
import org.zoomdev.zoom.common.exceptions.ZoomException;

import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.Date;
import java.util.concurrent.ConcurrentHashMap;

public final class ISchemaFactory {

    private static final ConcurrentHashMap<Type, ISchema> map = new ConcurrentHashMap<>();


    static ISchema create(Type type) {
        if (type instanceof Class) {
            return createFromClass((Class<?>) type);
        }

        if (type instanceof ParameterizedType) {
            ParameterizedType parameterizedType = (ParameterizedType) type;
            if (SchemaUtils.checkParameterizedType(parameterizedType)) {
                Class<?> schemaClass = (Class<?>) parameterizedType.getActualTypeArguments()[0];
                return createList(parameterizedType, (Class<?>) parameterizedType.getRawType(), schemaClass);
            }
        }

        throw new ZoomException("不支持的类型" + type);
    }


    static ISchema createFromClass(Class<?> type) {
        //basic
        ISchema schema = createFromSimple(type);
        if (schema != null) {
            return schema;
        }
        if (type.isArray()) {
            Class componentType = type.getComponentType();
            if (componentType == Object.class) {
                return UnknownArraySchema.IUnknownArray;
            }
            if (SchemaUtils.isSpecific(componentType)) {
                return new ArraySchema(componentType);
            }
            throw new ZoomException("不支持的类型"+type);
        }
        return createPojo(type);
    }

    private static ISchema createList(ParameterizedType type, Class<?> rawClass, Class<?> schemaClass) {
        return map.computeIfAbsent(type, k -> {
            ISchema schema = createFromSimple(schemaClass);
            if (schema != null) {
                return new CollectionSchema(rawClass, schema);
            }
            return new PojoListSchema(rawClass, RuntimeSchema.getSchema(schemaClass));
        });

    }


    static ISchema createFromSimple(Class<?> type) {
        //basic
        if (type == Integer.class || type == int.class) {
            return BasicSchema.INT;
        }

        if (type == Long.class || type == long.class) {
            return BasicSchema.LONG;
        }

        if (type == Short.class || type == short.class) {
            return BasicSchema.SHORT;
        }

        if (type == Byte.class || type == byte.class) {
            return BasicSchema.BYTE;
        }

        if (type == Boolean.class || type == boolean.class) {
            return BasicSchema.BOOL;
        }

        if (type == Float.class || type == float.class) {
            return BasicSchema.FLOAT;
        }

        if (type == Double.class || type == double.class) {
            return BasicSchema.DOUBLE;
        }

        if (type == String.class) {
            return BasicSchema.STRING;
        }


        if (type == Date.class) {
            return BasicSchema.DATE;
        }

        if (Enum.class.isAssignableFrom(type)) {
            return map.computeIfAbsent(type, k -> new BasicSchema.EmuSchema(type));
        }

        if (type == byte[].class) {
            return BasicSchema.BYTES;
        }


        return null;
    }


    private static ISchema createPojo(Class<?> type) {
        return map.computeIfAbsent(type, k -> new PojoSchema(RuntimeSchema.getSchema(type)));
    }

}
